package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartInfoServiceImpl implements CartInfoService {
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CartAsyncService cartAsyncService;


    //将商品加入购物车
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addToCart(Long skuId, String userId, Integer skuNmu) {
        String cartKey = this.getCartKey(userId);
        CartInfo cartInfoExist = null;

        try {
            //先从数据库中查看该用户的购物车中是否已经有该商品，有的话，更新数量，没有的话，新增数据
            /*QueryWrapper<CartInfo> cartQuery = new QueryWrapper<>();
            cartQuery.eq("sku_id",skuId);
            cartQuery.eq("user_id",userId);
            cartInfoExist = cartInfoMapper.selectOne(cartQuery);*/
            /*
            问题：添加时若只是把该条购物商品添加到缓存，存在漏洞，即，当缓存过期后，只添加了之后的购物
            车商品，之前的未添加上，所以需要，先从redis中验证该用户的购物车key是否已经过期(是否还有该key)，
            若没有过期，说明缓存中数据完整，直接从redis中获取，验证该条数据即可
            若已经过期，需将mysql中数据先同步到redis中
            */
            if (!redisTemplate.hasKey(cartKey)){
                this.getCartList(userId);
            }
            cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());

            if (cartInfoExist != null){
                cartInfoExist.setSkuNum(cartInfoExist.getSkuNum()+skuNmu);
                cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
                //更新 修改时间
                cartInfoExist.setIsChecked(1);
                cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
                //cartInfoMapper.updateById(cartInfoExist);
                cartAsyncService.updateCartInfo(cartInfoExist);
                //更改redis
            }else {
                CartInfo cartInfo = new CartInfo();
                SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
                cartInfo.setUserId(userId);
                cartInfo.setSkuId(skuId);
                cartInfo.setSkuNum(skuNmu);
                cartInfo.setSkuPrice(skuInfo.getPrice());
                //放入购物车时的价格
                cartInfo.setCartPrice(skuInfo.getPrice());
                cartInfo.setSkuName(skuInfo.getSkuName());
                cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));

                cartAsyncService.saveCartInfo(cartInfo);
                cartInfoExist = cartInfo;
                //放入redis
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        //添加或者更新
        redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfoExist);
        //添加或者更新时设置过期时间
        setCartKeyExpire(cartKey);
    }

    //展示购物车列表查询购物车
    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (!StringUtils.isEmpty(userId)){
            if (StringUtils.isEmpty(userTempId) && CollectionUtils.isEmpty(this.getCartList(userTempId))){
                cartInfoList = this.getCartList(userId);
                return cartInfoList;
            }
            List<CartInfo> cartTempList = this.getCartList(userTempId);
            cartInfoList = this.mergeToCartList(cartTempList,userId);
            this.deleteCartList(userTempId);
            return cartInfoList;
        }
        if (StringUtils.isEmpty(userId)){
            cartInfoList = this.getCartList(userTempId);
        }
        return cartInfoList;
    }

    //更改购物车商品选中状态
    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        cartAsyncService.checkCart(userId,isChecked,skuId);
        String cartKey = this.getCartKey(userId);
        CartInfo cartInfo = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());
        cartInfo.setIsChecked(isChecked);
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(),cartInfo);
        this.setCartKeyExpire(cartKey);
    }

    //删除某用户指定的购物车商品
    @Override
    public void deleteCartInfo(String userId, Long skuId) {
        cartAsyncService.deleteCartInfo(userId,skuId);
        String cartKey = this.getCartKey(userId);
        if (redisTemplate.boundHashOps(cartKey).hasKey(skuId.toString())){
            redisTemplate.boundHashOps(cartKey).delete(skuId.toString());
        }
    }

    //删除临时用户的购物车数据
    private void deleteCartList(String userTempId) {
        cartAsyncService.deleteCartInfoByUserId(userTempId);
        String cartKey = this.getCartKey(userTempId);
        if (redisTemplate.hasKey(cartKey)){
            redisTemplate.delete(cartKey);
        }
    }

    //临时购物车合并到登录的购物车
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNoLoginList, String userId) {
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);
        if (StringUtils.isEmpty(cartInfoLoginList)){
            for (CartInfo cartInfo : cartInfoNoLoginList) {
                cartInfo.setUserId(userId);
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                cartInfoMapper.insert(cartInfo);
            }
        }else {
            Map<Long, CartInfo> cartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
            for (CartInfo cartInfo : cartInfoNoLoginList) {
                Long skuId = cartInfo.getSkuId();
                if (cartInfoMap.containsKey(skuId)){
                    CartInfo cartInfoLogin = cartInfoMap.get(skuId);
                    cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum()+cartInfo.getSkuNum());
                    if (cartInfoLogin.getIsChecked().intValue()==1 || cartInfo.getIsChecked().intValue()==1){
                        cartInfoLogin.setIsChecked(1);
                    }
                    cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                    UpdateWrapper<CartInfo> cartInfoUpdateWrapper = new UpdateWrapper<>();
                    cartInfoUpdateWrapper.eq("user_id",userId);
                    cartInfoUpdateWrapper.eq("sku_id",skuId);
                    cartInfoMapper.update(cartInfoLogin,cartInfoUpdateWrapper);
                }else {
                    cartInfo.setUserId(userId);
                    cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                    cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                    cartInfoMapper.insert(cartInfo);
                }
            }
        }
        List<CartInfo> cartInfoList = this.loadCartCache(userId);
        return cartInfoList;
    }

    //封装通过userId 或者临时（userTempId）查询购物车
    private List<CartInfo> getCartList(String userId) {
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (CollectionUtils.isEmpty(cartInfoList)){
            cartInfoList = this.loadCartCache(userId);
            return cartInfoList;
        }else {
            cartInfoList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(),
                            o1.getUpdateTime(),Calendar.SECOND);
                }
            });
            return cartInfoList;
        }

    }

    //从数据库中查出该用户的购物车数据，且放到redis中
    public List<CartInfo> loadCartCache(String userId) {
        String cartKey = this.getCartKey(userId);
        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("user_id", userId);
        cartInfoQueryWrapper.orderByDesc("update_time");
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(cartInfoQueryWrapper);
        if (CollectionUtils.isEmpty(cartInfoList)){
            return new ArrayList<>();
        }
        HashMap<String,CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            cartInfo.setSkuPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
            map.put(cartInfo.getSkuId().toString(),cartInfo);
        }
        //批量放入缓存
        redisTemplate.opsForHash().putAll(cartKey,map);
        //设置过期时间
        this.setCartKeyExpire(cartKey);
        return cartInfoList;
    }


    //设置过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    //生成redis中对应用户的cart的key
    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX+userId+RedisConst.USER_CART_KEY_SUFFIX;
    }

    //订单列表确认购买商品列表
    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartList = this.getCartList(userId);
        if (!CollectionUtils.isEmpty(cartList)){
            cartList = cartList.stream().filter(cartInfo -> cartInfo.getIsChecked() == 1)
                    .collect(Collectors.toList());
        }
        return cartList;
    }
}
